home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgramD2.iso
/
Borland
/
Borland C++ V5.02
/
WINSOCK.PAK
/
DLGSTRM.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-05-06
|
14KB
|
405 lines
/*-----------------------------------------------------------------------*\
| OWLSock Demo For Windows v1.0 |
--------------------------------------------------------------------------|
| Written By: Paul Pedriana |
| Date: May 7, 1995. |
| Copyright: Copyright (c) 1995 by Paul Pedriana. All Rights Reserved. |
| UserID(s): 70541,3223 |
| 70541.3223@compuserve.com |
--------------------------------------------------------------------------|
| This OWLSock demo is an application that demonstrates some features |
| of OWLSock. It uses only asynchronous (non-blocking) Winsock calls, |
| and uses OWLSock socket 'external' notification rather than internal |
| notification. External notification is the way most Winsock apps do |
| FD_XXX notifications; see the OWLSock docs for more info. |
--------------------------------------------------------------------------|
| Notes on this module: |
| This module is a dialog box that implements a stream socket TCP |
| connection. It either connects to a given port and address by itself, |
| or it can accept an already connected socket, e.g. a socket created by |
| an accept call. |
| Run another instance of this dialog on another computer and you can |
| connect the two computers together and send data back and forth. |
\*-----------------------------------------------------------------------*/
#include <owl/pch.h>
#if !defined(OWL_INPUTDIA_H)
# include <owl/inputdia.h>
#endif
#if !defined(OWL_WINSOCK_H)
# include <owl/winsock.h>
#endif
#include <stdlib.h> //For atoi() call.
#include "dlgstrm.h"
//********************************************************************************************
DEFINE_RESPONSE_TABLE1(DlgSendStream, TDialog)
EV_CHILD_NOTIFY(IDC_BTN_SEND, BN_CLICKED, CmBtnSend),
EV_CHILD_NOTIFY(IDC_BTN_SEND_CLEAR, BN_CLICKED, CmBtnSendClear),
EV_CHILD_NOTIFY(IDC_BTN_RECEIVE_CLEAR, BN_CLICKED, CmBtnReceiveClear),
EV_CHILD_NOTIFY(IDC_BTN_CONNECT, BN_CLICKED, CmConnectDisconnect),
EV_MESSAGE(MSG_HOST_INFO_NOTIFY, DoHostNotification),
EV_MESSAGE(MSG_SOCKET_NOTIFY, DoSocketNotification),
END_RESPONSE_TABLE;
DlgSendStream::DlgSendStream(TWindow* parent, TResId resId, TModule* module)
:
TDialog(parent, resId, module),
myPresentState(nIdle),
sAddressToConnectTo(0, INADDR_NONE),
bDataSent(true)
{
TINetSocketAddress ourSocketAddress; //same as INetSocketAddress(0, INADDR_ANY);
myStreamSocket = new TStreamSocket(ourSocketAddress);
myStreamSocket->SetNotificationWindow(this); //redirect socket FD_XXX notifications to us.
}
DlgSendStream::~DlgSendStream()
{
delete myStreamSocket; //It should usually be already deleted.
}
void DlgSendStream::SetupWindow()
{
editAddressSend = new TEdit(this, IDC_EDIT_ADDRESS_SEND, 64);
editAddressReceive = new TEdit(this, IDC_EDIT_ADDRESS_RECEIVE, 64);
editDataSend = new TEdit(this, IDC_EDIT_SEND, 256);
editDataReceive = new TEdit(this, IDC_EDIT_RECEIVE, 256);
editPort = new TEdit(this, IDC_EDIT_PORT, 8);
staticStatus = new TStatic(this, IDC_STATIC_STATUS, 32);
btnConnectDisconnect = new TButton(this, IDC_BTN_CONNECT);
TDialog::SetupWindow();
}
//
// 'newConnectedSocket' is a valid and connected socket, usually connected with an Accept() call.
// This function causes this object to
// Returns true if able to take on the connected socket (with the given address).
// Usually will be unable to take on the newConnectedSocket if already connected with
// current socket.
//
short DlgSendStream::ConnectWithThis(TStreamSocket& newConnectedSocket)
{
char szPort[12];
int nError;
if (myPresentState != nIdle)
return 0; //Unable to comply, as we are busy.
*myStreamSocket = newConnectedSocket; //StreamSocket::operator=()
myStreamSocket->SetSaveSocketOnDelete(FALSE); //Now we own the socket descriptor.
sAddressToConnectTo = newConnectedSocket.PeerSocketAddress;
// The 'newConnectedSocket' may or may not be set up for Notification, so
// we specifically set it here to make sure it is the way we want it:
//
myStreamSocket->SetNotificationWindow(this);
nError = myStreamSocket->StartRegularNotification();
if (nError == WINSOCK_ERROR) {
MessageBox(TSocketError(TWinSock::Dll()->WSAGetLastError()).AppendError("Error on StartRegularNotification() in ConnectWithThis()."), "Error", MB_OK);
GoToIdleState();
return 0;
}
// Equivalent Winsock code:
//
nError = TWinSock::Dll()->WSAAsyncSelect(*myStreamSocket, *this, MSG_SOCKET_NOTIFY, FD_READ | FD_WRITE | FD_CLOSE | FD_CONNECT | FD_OOB);
if (nError) {
MessageBox(TSocketError(TWinSock::Dll()->WSAGetLastError()).AppendError("Error on WSASelect()"), "Error", MB_OK);
GoToIdleState();
return 0;
}
wsprintf(szPort, "%d", TWinSock::Dll()->ntohs(sAddressToConnectTo.GetPort()));
editPort->SetWindowText(szPort); //disable editing.
char far* szIPAddress;
szIPAddress = sAddressToConnectTo.ConvertAddress(sAddressToConnectTo.GetNetworkAddress());
editAddressSend->SetWindowText(szIPAddress);
GoToConnectedState();
return 1;
}
short DlgSendStream::ConnectWithThis(SOCKET& /*newSocket*/, TSocketAddress& /*sNewAddress*/)
{
//This function presently not defined.
return 0;
}
TResult DlgSendStream::DoHostNotification(TParam1, TParam2 param2)
{
int nError = WSAGETASYNCERROR(param2);
if (myPresentState != nWaitingForAddress)
return 1;
if (nError) {
MessageBox(TSocketError(nError).AppendError("Error on looking up address."), "Error", MB_OK);
GoToIdleState();
}
else{
sAddressToConnectTo.SetNetworkAddress(myHostInfoManager.HostEntry->GetINetAddress());
GoToConnectingState();
Connect(); //Tries to connect to the given address.
}
return 1;
}
TResult DlgSendStream::DoSocketNotification(TParam1, TParam2 param2)
{
int nEvent = (int)WSAGETSELECTEVENT(param2);
int nError = (int)WSAGETSELECTERROR(param2);
if (nEvent == FD_WRITE && !bDataSent) {
if (nError)
MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_WRITE."), "", MB_OK);
else
SendData();
}
else if (nEvent == FD_READ) {
if (nError)
MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_READ."), "", MB_OK);
else
ReadData();
}
else if (nEvent == FD_CONNECT && myPresentState == nConnecting) {
if (nError) {
MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_CONNECT."), "", MB_OK);
GoToIdleState();
}
else{
GoToConnectedState();
}
}
else if (nEvent == FD_CLOSE) {
if (nError)
MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_CLOSE."), "", MB_OK);
GoToIdleState();
}
return 1;
}
void DlgSendStream::Connect()
{
//First create the socket.
int nError = myStreamSocket->CreateSocket(); //similar to 'socket()'
if (nError == WINSOCK_ERROR) { //could also say 'if (nError) {'
MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error on CreateSocket()"), "Error", MB_OK);
GoToIdleState();
return;
}
//The StartRegularNotification() function converts socket into non-blocking socket.
myStreamSocket->StartRegularNotification();
//Bind is technically not really needed, as a connect() will bind for you:
nError = myStreamSocket->BindSocket();
if (nError == WINSOCK_ERROR) { //could also say 'if (nError) {'
MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error on BindSocket()"), "Error", MB_OK);
GoToIdleState();
return;
}
nError = myStreamSocket->Connect(sAddressToConnectTo);
if (nError == WINSOCK_ERROR) {
if (myStreamSocket->GetLastError() != WSAEWOULDBLOCK) {
MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error on Connect()"), "Error", MB_OK);
GoToIdleState();
return;
}
//Else, we will soon get asynchronously notified of a completed connection with FD_CONNECT.
}
}
void DlgSendStream::ReadData() {
char szData[256];
int nLength=255;
int nEditLength;
int nError;
nError = myStreamSocket->Read(szData, nLength);
if (nError == WINSOCK_ERROR) {
MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error trying to read data."), "", MB_OK);
return;
}
szData[nLength]=0; //Null-terminate the data to make it a C-string.
nEditLength = editDataReceive->GetWindowTextLength();
editDataReceive->SetSelection(nEditLength, nEditLength); //Move cursor to end of text.
editDataReceive->Insert(szData); //Append the new text.
}
void DlgSendStream::SendData() {
int nError;
int nCharsToSend;
nCharsToSend = lstrlen(szDataToSend);
nError = myStreamSocket->Write(szDataToSend, nCharsToSend);
if (nError == WINSOCK_ERROR) {
if (myStreamSocket->GetLastError() != WSAEWOULDBLOCK)
bDataSent = TRUE;
MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error trying to write data."), "", MB_OK);
return;
}
bDataSent = TRUE;
MessageBox("Send succeeded", "", MB_OK);
}
void DlgSendStream::CmConnectDisconnect() {
short bAddressIsDottedDecimal;
char szAddressInput[256];
u_short nPortEnteredByUser;
HANDLE hHostRequest;
int nError;
if (myPresentState == nIdle) {
GoToWaitingForAddressState();
//Get port entered by user.
editPort->GetWindowText(szAddressInput, 255);
nPortEnteredByUser = TWinSock::Dll()->htons((u_short)atoi(szAddressInput)); //need an 'atous()' function.
sAddressToConnectTo.SetPort(nPortEnteredByUser);
//Get actual address entered by user.
editAddressSend->GetWindowText(szAddressInput, 255);
bAddressIsDottedDecimal = sAddressToConnectTo.IsAddressDottedDecimal(szAddressInput);
if (bAddressIsDottedDecimal) {
sAddressToConnectTo.SetNetworkAddress(sAddressToConnectTo.ConvertAddress(szAddressInput));
//We can immediately move on to the 'nConnecting' state.
GoToConnectingState();
Connect();
}
else{
nError = myHostInfoManager.GetHostInfoAsync(*this, hHostRequest, szAddressInput);
if (nError == WINSOCK_ERROR) {
MessageBox(TSocketError(myHostInfoManager.GetLastError()).AppendError("Unable to convert the host name to an address."), "Error", MB_OK);
GoToIdleState();
}
}
}
else if (myPresentState == nWaitingForAddress) {
//cancel the lookup and go back to idle
myHostInfoManager.CancelHostRequest();
GoToIdleState();
}
else if (myPresentState == nConnecting) {
//cancel the connection attempt and go back to idle.
//I don't know how to cancel a connection attempt...
//MessageBox("Busy connecting", "", MB_OK);
//Maybe this will work (?):
int nError;
nError = TWinSock::Dll()->WSACancelBlockingCall();
if (nError == SOCKET_ERROR) {
nError = TWinSock::Dll()->WSAGetLastError();
MessageBox(TSocketError(nError).AppendError("Can't cancel connection attempt."), "", MB_OK);
}
else
GoToIdleState();
}
else if (myPresentState == nConnected) {
//simply disconnect and return to idle.
myStreamSocket->ShutDownSocket();
GoToIdleState();
}
}
void DlgSendStream::CmBtnSend() {
if (myPresentState != nConnected) {
MessageBox("This socket is not connected", "", MB_OK);
return;
}
editDataSend->GetWindowText(szDataToSend, 256);
if (szDataToSend[0]) {
bDataSent = FALSE;
SendData();
}
else
MessageBox("There is nothing to send.", "", MB_OK);
}
void DlgSendStream::CmBtnSendClear() {
editDataSend->Clear(); //Clear the text.
}
void DlgSendStream::CmBtnReceiveClear() {
editDataReceive->Clear(); //Clear the text.
}
void DlgSendStream::CmOk() {
if (myPresentState != nIdle) {
CmConnectDisconnect(); //Disconnect/Cancel the operation and return all to idle.
}
TDialog::CmOk();
}
void DlgSendStream::GoToIdleState() {
bDataSent = 1;
myPresentState = nIdle;
staticStatus->SetWindowText("Status: Idle");
btnConnectDisconnect->SetWindowText("Connect");
editPort->SetReadOnly(FALSE); //enable editing.
editAddressSend->SetReadOnly(FALSE);
editAddressReceive->Clear();
//We close the socket here if it was open already.
myStreamSocket->CloseSocket();
}
void DlgSendStream::GoToWaitingForAddressState() {
myPresentState = nWaitingForAddress;
staticStatus->SetWindowText("Status: Looking Up Address...");
btnConnectDisconnect->SetWindowText("Cancel");
editPort->SetReadOnly(TRUE); //disable editing.
editAddressSend->SetReadOnly(TRUE);
}
void DlgSendStream::GoToConnectingState() {
myPresentState = nConnecting;
staticStatus->SetWindowText("Status: Connecting...");
//Not necessary, because previous state already set this:
//btnConnectDisconnect->SetWindowText("Cancel");
//editPort->SetReadOnly(TRUE); //disable editing.
//editAddressSend->SetReadOnly(TRUE);
}
void DlgSendStream::GoToConnectedState() {
char szIPAddress[20];
myPresentState = nConnected;
staticStatus->SetWindowText("Status: Connected");
btnConnectDisconnect->SetWindowText("Disconnect");
//Actually necessary, because we could go directly from nIdle to
// nConnected with the ConnectWithThis() function:
editPort->SetReadOnly(TRUE); //disable editing.
editAddressSend->SetReadOnly(TRUE);
editAddressSend->GetWindowText(szIPAddress, 20);
editAddressReceive->SetWindowText(szIPAddress);
}